/*
 * Copyright (c) 2015. For Intelligent Group.
 */

package com.intelligent.ispc.security;


import com.intelligent.ispc.common.mapper.BeanMapper;
import com.intelligent.ispc.common.utils.ApplicationException;
import com.intelligent.ispc.common.utils.Digests;
import com.intelligent.ispc.common.utils.Encodes;
import com.intelligent.ispc.core.dto.CredentialsUserDto;
import com.intelligent.ispc.core.dto.RoleDto;
import com.intelligent.ispc.core.dto.SecurityUserDto;
import com.intelligent.ispc.core.dto.UserDto;
import com.intelligent.ispc.core.service.SecurityService;
import com.intelligent.ispc.utils.Constant;
import com.intelligent.ispc.utils.SystemConfig;
import com.intelligent.ispc.utils.UserStatus;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.util.Date;

/**
 * Class description goes here.
 */
public class ShiroDbRealm extends AuthorizingRealm {

    @Autowired
    protected SecurityService securityService;

    @Autowired
    protected SystemConfig systemConfig;

    /**
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        CredentialsUserDto userDto = null;
        try {
            UsernamePasswordToken token = (UsernamePasswordToken) authcToken;

            userDto = securityService.findRefreshUser(token.getUsername());

            if (userDto != null) {
                if (userDto.getStatus() != null && userDto.getStatus().getCode().equals(UserStatus.DISABLE.getCode())) {
                    throw new DisabledAccountException();
                }

                if (userDto.getStatus() != null && userDto.getStatus().getCode().equals(UserStatus.LOCK.getCode())) {
                    throw new LockedAccountException();
                }

                SecurityUserDto sUserDto = new SecurityUserDto();
                BeanMapper.copy(userDto, sUserDto);

                byte[] salt = Encodes.decodeHex(userDto.getSalt());
                byte[] accountCredentials = Digests.sha1(String.valueOf(token.getPassword()).getBytes(), salt, SecurityService.HASH_INTERATIONS);

                if (!userDto.getPassword().equals(Encodes.encodeHex(accountCredentials))) {
                    throw new IncorrectCredentialsException();
                }

                if (userDto.getRoles() != null && userDto.getRoles().isEmpty()) {
                    throw new ConcurrentAccessException();
                }


                sUserDto.setRoles(userDto.getRoles());


                return new SimpleAuthenticationInfo(sUserDto, userDto.getPassword(), ByteSource.Util.bytes(salt), getName());
            } else {
                throw new UnknownAccountException();
            }
        } catch (ApplicationException ex) {
            throw ApplicationException.unchecked(ex);
        }
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SecurityUserDto securityUserDto = (SecurityUserDto) principals.getPrimaryPrincipal();

        UserDto userDto = securityService.findByLoginName(securityUserDto.getLoginName());

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        for (RoleDto role : userDto.getRoles()) {
            if (Constant.ROLE_DISABLE.equals(role.getEnabled()))
                continue;
            info.addRole(role.getName());
            info.addStringPermissions(role.getPermissions());
        }

        return info;
    }

    @PostConstruct
    public void initCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(SecurityService.HASH_ALGORITHM);
        matcher.setHashIterations(SecurityService.HASH_INTERATIONS);

        setCredentialsMatcher(matcher);
    }

}
